package com.thinkingcoder.tcregistry.service;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import com.thinkingcoder.tcregistry.cluster.Snapshot;
import com.thinkingcoder.tcregistry.model.meta.InstanceMeta;

import lombok.extern.slf4j.Slf4j;

/**
 * Default implementation of RegistryService.
 */
@Slf4j
public class TcRegistryService implements RegistryService {

    public static MultiValueMap<String, InstanceMeta> REGISTRY = new LinkedMultiValueMap<>();

    public static Map<String, Long> VERSIONS = new ConcurrentHashMap<>();

    public final static Map<String, Long> TIMESTAMPS = new ConcurrentHashMap<>();

    public final static AtomicLong VERSION = new AtomicLong(0);

    @Override
    public synchronized InstanceMeta register(String serviceName, InstanceMeta instance) {
        List<InstanceMeta> instances = REGISTRY.get(serviceName);
        if (instances != null && !instances.isEmpty()) {
            if (instances.contains(instance)) {
                log.info(" ===> instance {} already exists", instance.toUrl());
                instance.setStatus(true);
                return instance;
            }
        }

        log.info(" ===> register instance {}", instance.toUrl());
        REGISTRY.add(serviceName, instance);
        instance.setStatus(true);
        renew(instance, serviceName);
        VERSIONS.put(serviceName, VERSION.incrementAndGet());
        return instance;
    }

    @Override
    public synchronized InstanceMeta unregister(String serviceName, InstanceMeta instance) {
        List<InstanceMeta> instances = REGISTRY.get(serviceName);
        if (instances == null || instances.isEmpty()) {
            return null;
        }
        log.info(" ===> unregister instance {}", instance.toUrl());
        instances.removeIf(temp -> temp.equals(instance));
        instance.setStatus(false);
        renew(instance, serviceName);
        VERSIONS.put(serviceName, VERSION.incrementAndGet());
        return instance;
    }

    @Override
    public List<InstanceMeta> getAllInstances(String serviceName) {
        return REGISTRY.get(serviceName);
    }

    @Override
    public synchronized Long renew(InstanceMeta instance, String... serviceNames) {
        Long now = System.currentTimeMillis();
        for (String serviceName : serviceNames) {
            TIMESTAMPS.put(serviceName + "@" + instance.toUrl(), now);
        }
        return now;
    }

    @Override
    public Long version(String serviceName) {
        return VERSIONS.get(serviceName);
    }

    @Override
    public Map<String, Long> versions(String... serviceNames) {
        return Arrays.stream(serviceNames).collect(Collectors.toMap(k -> k, v -> VERSIONS.get(v), (k1, k2) -> k2));
    }

    public static synchronized Snapshot snapshot() {
        LinkedMultiValueMap<String, InstanceMeta> registry = new LinkedMultiValueMap<>();
        registry.addAll(REGISTRY);
        Map<String, Long> versions = new HashMap<>(VERSIONS);
        Map<String, Long> timestamps = new HashMap<>(TIMESTAMPS);
        return new Snapshot(registry, versions, timestamps, VERSION.get());
    }

    public static synchronized long restore(Snapshot snapshot) {
        REGISTRY.clear();
        REGISTRY.addAll(snapshot.getREGISTRY());
        VERSIONS.clear();
        VERSIONS.putAll(snapshot.getVERSIONS());
        TIMESTAMPS.clear();
        TIMESTAMPS.putAll(snapshot.getTIMESTAMPS());
        VERSION.set(snapshot.getVersion());
        return snapshot().getVersion();
    }
}
